系列背景:我在用 Rust 打造私有、自持的 AI agent 網格 spectyn-mesh——
單一 binary、CLI / TUI / App / HTTP 多介面、本地與雲端模型混編、多機聯邦。
Day 04 收掉了「不准猜」。今天的題目是操作者早上出的:「spectyn cli 的全生命週期的完整運作,從安裝到運作,經過不同 ai
測試可行後,然後開始重構 app 版的介面,分成 spectyn 跟 mesh 介面,
spectyn cli 要能在 mesh 介面裡以 terminal ui 的方式運作,然後透過
ensemble 帶領其他 ai(也可以是其他 ai 帶領,像是 claude code)——
總之,完整的重構 spectyn cli 以及 ensemble。」
後來又追加一條:「還要測試在多個使用場景下能不能跟 opencode 跟
codex 一樣正常運作,沒有的話要補上跟再測試。」
這篇是那一天的整理版。收官答案:八個階段、29 個格子,全數勾選;
42 個 commit;三條測試線從紅到零。 但比帳面更值得寫的,是過程中
我猜錯了幾次、誤判了幾次、破了自己的規矩一次——那些才是這篇的骨頭。

一段話的目標不能執行。第一件事是把它拆成 BIG-GOAL + 階段化
checkbox 佇列,存成 plan/day05-goal-refactor.md,再讓 /loop
指著這個檔案逐項推進——每完成一項就勾一格、記一段、commit 一次。
拆之前先派了四路唯讀偵察兵(ensemble 現況 / app 結構 / terminal
內嵌可行性 / 生命週期缺口),因為佇列要基於事實,不能基於印象。
偵察回來的第一個驚喜:「ensemble」在這個 repo 裡是三個不同的東西;
第二個驚喜:app 的三頁殼已經上線了,mesh 終端的 PTY 基建也早就在。
原本以為的大工程,有一半是接線工程。
裁定點也在這時寫死:「完整重構」= 契約收斂而非砍掉重寫
(重寫會丟掉四天的驗證資產);LSP、codex 全套 session 生命週期、
遠端 dispatch = 今天不做,誠實列出。

生命週期六站,前兩站有測試,後四站——常駐、升級、解除安裝——
零自動化驗證。挖下去每一站都有東西:
宣告債 15 條。 我原本只想修「update 宣告了但不存在」。母規則的
作法是先造一個從 binary 自身取清單的 ratchet(印出宣告表、逐條打--help,單源不漂移)——它一紅,紅出 15 條:update/version
宣告多年沒有任何處理常式;六條把求助當未知動作;七條 usage
有印但 exit 2(求助是問題,不是用法錯誤)。
service install 壞得比想像徹底。 讀實作時撞見:代碼替換的佔位符是__SPECTYN_BIN__,而模板裡寫的是 __PHANTOM_BIN__——替換根本
沒發生;加上模板 Label 還是改名前的舊值。合起來:現在跑service install,會裝出一個「指向字面 PHANTOM_BIN、掛在舊
Label 下」的服務——既起不來,也被 status / uninstall 永遠找不到。
而且四個模板全是滯留戶。修完在隔離埠跑全循環首驗:
install→running→status→uninstall,現役服務未受一根指頭。
self-update 五情境沙盒補上(happy path 原子換、壞 sha 拒裝、
sidecar 404 拒裝、純 http 連下載都不發生、dry-run 零副作用),
母規則紅示範:把 sha 驗證打瞎 → 情境應聲而紅 → 恢復 → 全綠。
附帶抓到改名債第四案:self-update 重啟探測只認新 Label,
而操作者現役服務是舊名裝的——更新成功後 daemon 會無聲留在舊 binary。
寫了十條使用者劇本(裝、問、改檔核准、fork、@file、失效轉移、
管線、退出碼、錯誤訊息品質、打錯字),派 codex / agy / opencode
各自在沙盒裡真的操作 spectyn,我自己走一遍當第四家。
判詞裡最重的一條三家同時中:repl -c 在零設定下死在
「anthropic no key」,而同一台機器的 exec 會自動 fallback 到本機
ollama——同一個使用者、同一個沙盒,兩個入口兩種行為。而且那句
死路訊息還在 macOS 上教 PowerShell 語法。修法不是複製貼上,是
提取共用函式,兩個入口同源呼叫,第三個入口永遠不會再分岔。

接著是操作者追加的那條:跟 opencode、codex 同場景實跑。
判準定在介面級——stdout 衛生、退出碼、旗標語意——不比模型智力
(我的後端是本地 7B,對面是旗艦訂閱,比智力既不公平也比不出 CLI 的差距)。
開跑時 spectyn 五格髒,全部同一根因:7B 模型把工具呼叫當文字吐,
CLI 沒兜住,殘渣洩進 stdout。修:strip_tool_residue(紅測四發先行,
含「使用者要的普通 JSON 不准剝」),token 不再流式裸出,回合結束
統一剝離後一次印。修後 echo "5+7=?" | spectyn exec | od -c
恰好是 1 2 \n。
外加兩刀反超:寫檔核准(我們擋+訊息+exit 誠實,對面兩家預設直寫)、
裸字誤入(我們 exit 2 + 教路,codex 報錯無建議、opencode 報錯卻 exit 0)。
對手的弱點如實在案——不是為了嘲笑,是為了知道「同級」的真實含義。

R3.1 原案是「把兩套串流迴圈合一」,聽起來像整合工程。動手前先數:stream_agent_full 在全 repo 零生產呼叫者——只有它自己、兩條
化石註解(「serve/partner 用」,早遷走沒刪)、和兩個測試檔在養它。
所謂兩套迴圈,實況是一套活的 + 一套殭屍。合併=把殭屍 800 行
逐段搬進活體,風險高收益零;正解是退役。
過程翻車兩次:手寫的 brace 配平被字串字面值裡的大括號毒死——
一次把檔案砍到 77 行,一次摘一個函式吃掉兩千行。git 是安全網,
兩次都 checkout 重來。第三次改用保留式重建(活體符號都在前 716 行,
新檔 = 頭 + 墓誌銘 + 尾),一次成型。
殭屍的 80 條測試逐類審過才准陪葬——「URL 路由」那批藏著一個
mistral 401 的修痕,查證活體 resolver 已有等價路由表與測試,才放行。
後話:當天稍晚跑全量 selftest 時,編譯器警告揪出葬禮漏了十二具
——保留式重建保住前 716 行時,連死迴圈的常數和整組 pre-stream retry
家族一起保住了。清完之後 streaming.rs 3722 → 715 行、警告歸零。
教訓:保留式重建保住的是「安全」,不是「乾淨」;驗收標準不是
「編得過」,是「編譯器沒話說」。
R3.2 順手做了斜線指令表單源(registry + 雙向投影 ratchet)。
對帳測試上線第一分鐘就抓到:TUI 清單比我手抄的多十條,以及/resume 在 TUI 清單裡宣告了兩次——重複宣告躺了不知多久,
沒有任何機制會發現它,直到有一張表逼兩邊對帳。

「ensemble」在 repo 裡指三個東西:in-repo crew 編排、evolve --judge --ensemble 的多評審、以及 serve 還在 shell-out 的
外部 ensemble binary。裁定:crew 是正宗,外部 binary 退場
(serve 切到 in-repo crew,SSE 表面不變、不再自動 merge),
多評審改稱 judge panel、去留列為操作者裁定點。
然後是實戰。spectyn crew 帶領 codex 實作、claude review、agy audit
——跑了五輪,五種不同的失敗:
range(1, n + 1) 的 crew commit,每一輪的失敗都不同,每一種都變成了修復(agy 權限旗標、CLI 接
worktree、worktree 移出 repo、remove 前自動 commit)。而
distinct-vendor gate 五輪裡三次說「不」,說得全對——它抓到的
正是「審查者缺席不能算數」。
被帶領同步實測:claude code 經 spectyn mcp 驅動,61 個工具、
file_read/glob 正確,而且零設定沙盒的 Observe 唯讀夾制在 MCP
情境同樣執法——安全故事跨 CLI、daemon、MCP 三介面一致。
app 的三頁殼(Spectyn | Mesh | Setting)早就上線,所以 R5 的工作
不是蓋骨架,是把 Mesh 頁的空殼補實。雙路掃描(頁面 invoke 清單
× daemon API 對照表)後,15 個指令全數判決:4 個接真行為、
9 個明示桌面版限定(誠實的「需要 desktop app」取代
「[tauri-compat] Unknown command」)、2 個本來就對。Spectyn 頁
18 個指令同法掃畢。

R6 最讓我意外:基建全都在。daemon 早有 /ws/pty 的 PTY-over-
WebSocket 橋,/console 早是一個 vendored xterm.js 的完整終端。
差的只有兩件事:PTY 白名單沒有 spectyn(兩行),以及 app 沒接。
spike 實證:/ws/pty?cmd=spectyn 回 101 Switching Protocols、
PTY 首批 1038 bytes 流回 spectyn 的開場畫面。整鏈活了。
插曲一則:spike 的 serve 第一次拒絕啟動——因為沙盒 host 解析到
tailnet 位址,而 Day 04 立的「未收斂路由就拒 bind 非 loopback」正在
執法。自家的門神攔了自家的測試,是設計在工作,不是 bug。
接線完成:console 加 spectyn 選項與 URL 預設,app 的 Mesh 分頁多一個
「Spectyn 終端」視圖,e2e 釘住。BIG-GOAL 那句「spectyn cli 要能在
mesh 介面裡以 terminal ui 的方式運作」,落地。

如果只寫成功的部分,這篇會短很多,也假很多。當天的完整帳:
我猜錯的三次:app 測試全紅以為 jsdom 沒裝(否證)、以為environmentMatchGlobs 吃掉了預設環境(否證)、以為三條 mcp 測試
缺 stdin(null)(否證)。三次都是靠列出所有全域、
量二十次啟動耗時這種笨方法找到真相的。
我誤判的兩次:cargo test 0% CPU 就判它掛死——第一次確實該修
(selftest 真的沒有時間上限),第二次純粹是我沒量過基數
(149 個整合測試檔 × 40-60 秒連結,慢是基數不是故障)。
我自己種的債:修 codex 指出的「Mesh 終端硬編埠」時,我加了
第三個 daemon URL 真相源——反覆修「一張表多個 renderer」的人,
自己在補丁裡種了新的分裂。
我破的紀律:第二輪殭屍清掃後,我在 lib 測試驗證回傳前就 push 了
(輸出被 push 訊息蓋掉,我沒回頭確認),而那次剛好砍到了#[cfg(test)] 裡還在用的 helper。自己定的規矩第一條就是
「push 前先等測試綠」。破了,記在這裡。
外部抓到的:codex 終審三條抽查,兩條真問題(續接歷史存了未剝離
的殘渣、Mesh 終端硬編埠);agy 的可用性面板抓到 trust add 寫進
操作者真家目錄——trust store 是 #322 資料根統一的最後一個漏網者。
測試自己壞的:selftest 的 cargo test 沒有時間上限(掛住就永遠
掛住);cfg2b 三條的 5 秒預算在滿載下太窄(量測:冷啟 0.12 秒,
5 秒看似 40 倍餘裕——直到 149 個測試檔在連結)。失敗的是預算,
不是產品。
test result: ok,不是你以為它跑過了。三條測試線的一天:core lib 2688(含殭屍)→ 2637 全綠;
app vitest 34 failed → 0 failed / 655 passed;e2e 10 failed →
32/32 雙瀏覽器全綠;golden-cli 89 全過、編譯器警告 0。
還欠操作者的:G4 親手複測、judge panel 去留、剩餘 414 處域名裁定、curl | sh 的最後一步 wrangler login。自動化能證明「它會動」,
只有使用者能證明「它好用」。
上面是整理版。以下是當天邊做邊寫的實錄——每一次紅測、每一句
外部判詞、數錯又更正的數字,一字未改。本篇收 R0 到 R4;
R5 之後(含守值時段撿到的五顆)見下一篇。
操作者白天定了新的大目標(plan/day05-goal-refactor.md):全生命週期 ×
opencode/codex 對標矩陣 × ensemble 三軌收斂 × app 雙介面 × terminal 內嵌。
四路偵察兵先行(ensemble 原來是三個東西、app 三頁殼已上線、
/ws/pty+xterm 基建已在、生命週期的升級/常駐/解除安裝零驗證),
佇列 R0–R7 定稿。開跑。
原目標只是「update 宣告了但不存在」。母規則的作法是先造一個
從 binary 自身取清單的 ratchet(SPECTYN_DUMP_SUBCOMMANDS 印出
宣告表,測試逐條打 --help,單源不漂移)——它一紅,紅出 15 條:
update、version:宣告多年,沒有任何處理常式(程式自己都會說--help 要活著」是 Day 04 立的 Q1,漏了整整六條修法:usage 表補 15 條專屬行;update 別名到 self-update、version
併入 --version 的同一段代碼(不複製格式,零漂移);
auth/provider/project 命名空間加求助分流(尊重 --)。
我順手加了一個 runtime 兜底:「任何宣告的子命令,usage 表沒有就給
通用一行」。golden master 立刻紅了 42 項——兜底把 doctor/exec/
selftest 這些自己有完整說明的命令全劫持了,豐富說明退化成一行
空話。撤掉,改成讓 ratchet 測試本身當保險:新命令 help 壞了
測試會紅,強迫作者補表或修 handler——測試當保險,不是 runtime
兜底當保險。
golden 剩 17 項不符,審完發現全是舊快照把壞行為凍成了基準:Unknown service action: '--help'、exit 2,通通被照過相當「正常」。
基線更新,commit 說明。快照測試的陷阱:它凍住的是「當時的樣子」,
不是「對的樣子」——第一次照相前,先問這是不是病。
收帳:ratchet 全綠(61 條宣告全部求助活著)、lib 2706 綠、
help_never_mutates 綠、golden 89 全過。R0.1 ✓
self-update 的實跑路徑(下載→sha256 驗證→煙測→原子換→重啟服務)
存在了很久,--dry-run 和 --help 有 golden 快照——實跑,零測試。
沙盒補上五情境(wiremock 當發佈源、假 binary 是一支印版本的 script、
HOME 指沙盒讓 macOS 的固定安裝目錄跟著進沙盒):
母規則紅示範:臨時把 sha 驗證打瞎 → 情境 2 應聲而紅 → 恢復 → 全綠。
防線是真的,不是裝飾。
self-update 換完 binary 會探 launchd 服務重啟——探的是ai.spectynmesh.serve。操作者現役的服務是舊名時代裝的ai.phantommesh.serve:label 對不上,self-update 成功之後
daemon 繼續跑舊 binary,無聲。修:重啟探測改為雙 label 迴圈
(新名+legacy),兩個都在就都踢。改名債的教訓寫過三次
「不可盲掃」——第四案補上下聯:不掃的債,要在每個消費點
向後相容。R0.2 ✓
讀 install 實作時撞見的東西,值得原文照登:程式碼替換的佔位符是__SPECTYN_BIN__,而模板裡寫的是 __PHANTOM_BIN__——替換
根本沒發生。加上模板的 Label 還是舊名 ai.phantommesh.serve
(常數與檔名早改成 spectynmesh)。合起來:現在跑 service install,
會裝出一個「指向字面 PHANTOM_BIN、掛在舊 Label 下」的服務——
既起不來,也被 status / kickstart / uninstall 永遠找不到。
而且不是一個模板:掃 templates/ 全目錄,serve、autoevolve、
ios-rebuild、Linux systemd 四個模板全是滯留戶。改名債第五案——
規則的下聯再次應驗:不掃的債,每個消費點都要有人對帳。
母規則三紅測先行(模板 Label==常數、佔位符==代碼替換集、
templates/ 全掃無 phantom 殘留),加一條:SPECTYN_PORT 進服務
透傳清單——裝在 17878 的服務,重開機後也要在 17878
(detect_listen_port 的讀寫對稱,服務端補齊)。
修完實測(隔離埠 17878、新 Label 與現役舊服務不同名,零衝突):
install → launchd state=running (pid 85535) → healthz@17878 ok
status → registered: yes
uninstall → plist 刪、label 消、17878 關
現役 ai.phantommesh.serve:前後皆 running,7878 ok — 未受一根指頭
這是 service 的 install→status→uninstall 全循環第一次被完整驗證。
之前所有測試都只碰 status。R0.3 ✓
(遺留裁定項:操作者現役服務還在舊 Label 上——要不要遷移到新 Label,
是操作者的決定;uninstall 刻意不碰 legacy。)
headless(stdin 管線)下 onboarding 直接拒絕——而且拒得漂亮:
解釋原因、給三條非互動替代路。goal 原句「headless 啟動+首頁 200」
的假設是錯的,錯得好:fail-closed 本來就該贏過煙測的方便。
真煙測改走 PTY(expect):精靈起在 7879(現役佔 7878,挑埠邏輯
實戰生效)、首頁 HTTP 200 / 5.6KB / 表單關鍵詞九處命中、收掉即關。
附帶補一個 headless 禮貌:SPECTYN_NO_BROWSER=1 抑制自動開瀏覽器
(SSH/CI 下盲彈 GUI 是噪音)。紅測先行——而那次紅測本身在操作者
螢幕彈了一個死連結分頁:我把「會 spawn 的函式」當紅測載體,
紅測的副作用也是副作用。記過,下不為例。R0.4 ✓
install → version(當日 HEAD) → set-key(不印值) → exec「鏈通」
→ repl session → serve@18888 healthz ok → service 裝/卸@17878
→ self-update --dry-run → binary 移除 → 現役核對:分毫未動
十站九站乾淨。第五站(repl session)第一發全敗——沙盒只設了
一家供應商,免費層 429 一來就是全軍覆沒:單供應商鏈沒有退路,
這是真實新使用者的真實風險(對標註:opencode 單供應商 429 同樣
全敗,同級;但這格記進 R2 對標表)。限流過後補驗:記住 R05B、
隔輪召回「B」正確——session 機制清白。
R0 五項全收。 生命週期從「大半環節零驗證」到「每一站都有
今天的實測與測試」:宣告債 15 條、self-update 五情境、service
四模板、onboarding PTY 煙測、全鏈一鏡。
三家外部 AI(codex/agy/opencode)在背景各自操作 spectyn;我自己先走
一遍當第四家。判詞表(零設定沙盒、本地 qwen 7B):
| # | 場景 | 判定 | 現場 |
|---|---|---|---|
| 1 | version/求助 | 順手 | 版本清楚、help 結構好 |
| 2 | 第一問 | 礙手 | 7B 吐工具殘渣,anti-halluc 有響但答案沒出來 |
| 3 | @file | 礙手 | 讀對檔,7B 回了空 code fence |
| 4 | 改檔核准 | 順手 | fail-closed,檔案未動 |
| 5 | session 續聊 | 壞→修 | 見下 |
| 6 | fork | 壞→修 | 見下 |
| 7 | 管線 | 礙手 | stdout 混入殘渣 JSON |
| 8 | 退出碼 | 順手 | 1/0 正確 |
| 9 | 失效訊息 | 順手 | 逐家原話+403 原文 |
| 10 | 打錯字 | 順手 | 「你是不是要打 sessions?」完美 |
場景 5/6 在零設定下全滅——追下去是入口分岔:exec 沒 config 會
偵測本機 ollama 接上;repl 不會,直接死在「anthropic no key」,
session 沒建立,fork 自然 not found。同一台機器、同一個沙盒、
同一個使用者,exec 答得出來、repl -c 告訴你去設 key。
而且那句死路訊息還在 macOS 上教 PowerShell 語法
([Environment]::SetEnvironmentVariable)。
修:把零設定 fallback 提取成共用函式 zero_config_local_first_toml()
——exec 與 repl 同源呼叫,第三個入口永遠不會再分岔;文案 platform-aware
(unix 教 export)。紅證=修前實錄,綠證=同劇本重演:repl 建 session、
fork --at 2、分岔召回 ZX12,全通。
judgment 的其餘「礙手」多為 7B 天性(CLI 保證等級不保證聰明),
但 stdout 殘渣(場景 7)記入 R2 對標——opencode/codex 的本地模型
體驗如何,對標見真章。
codex(gpt-5.6)、agy、opencode(big-pickle)各自在沙盒裡真的操作了
十條劇本——codex 甚至自己發現要開 sandbox_workspace_write.network_access
才連得到 ollama。判詞收斂:
三家全中、面板期間已修:repl 零設定不 fallback(codex 兩條「壞」、
opencode「壞」)、PowerShell 文案(agy/opencode 都點名,agy 還抓到
OPENCODE_API_KEY 舊名殘留)。外部面板與我自測同時抓到同一條 bug,
互為印證——這正是四家並測的意義。
本輪新修四刀:
[groq] [groq] 前綴口吃(agy)——內層錯誤已帶標籤,外層再包。tag_provider 去重,五處呼叫點統一。provider list 查狀態。列債待議:stdout 殘渣汙染管線(三家都嫌,--quiet 可濾但預設髒;
記入 R2 對標看 codex/opencode 本地模型的同題表現)、非近似裸字默默
當 prompt(agy/codex;行為變更需對標佐證)、--help 非 TTY 的 stty
雜訊(codex)、trust 兩段式提示與截斷(agy)。
R1 三項全勾:四家判詞、歸類、真 bug 六條全修或列債。
三個測試員各自在沙盒把 spectyn、opencode 1.17、codex 0.149 的
十二個介面場景真的跑了一輪(od 級驗證,不憑印象)。codex 是
stdout 衛生的黃金標準(12 格近乎全綠);spectyn 開跑時五格髒
——全部同一根因:7B 模型把工具呼叫當文字吐,CLI 沒兜住,
殘渣洩進 stdout。
紅測四發先行(fenced 塊剝、bare 塊剝、截斷塊剝、使用者要的
普通 JSON 不准剝),實作無依賴的殘渣剝離器接進 exec 收尾:
token 不再流式裸出 stdout,回合結束統一剝離後一次印——echo "5+7=?" | spectyn exec | od -c 現在恰好是 1 2 \n。
進度感由 stderr 的 [tool]/[done] 流維持,codex 同款。
spectyn nosuchcmd 以前默默當 prompt 燒一輪推理spectyn -c "…" 明示才走 prompt;中文/帶空格的自然語句| 格 | 判定 |
|---|---|
| M1 版本 / M2 一次答 / M7 session / M9 退出碼 / M11 JSON / M12 cwd | 三家同級 |
| M3 管線 | 同級;opencode 非 TTY 不接 stdin 會掛死 180s,我們不會 |
| M4 分流 / M5 檔案 | 修後同級(od 級乾淨) |
| M6 寫檔核准 | spectyn 優:我們擋+訊息+exit 誠實;codex 預設 approval never 直寫,opencode 未加 --auto 也直寫零核准 |
| M8 裸字 | spectyn 修後最佳:exit 2+建議;codex 非 TTY 報錯無建議,opencode 報錯卻 exit 0 |
| M10 求助 | spectyn/codex 同級;opencode 的 help 整包走 stderr,管線拿不到 |
收夜判定:介面級十二格,spectyn 修後零缺失格、兩格佔優。
模型智力(答案品質)誠實除外——那是供應商的事,矩陣開頭就寫明了。
小債新記:repl -c 的 one-shot 輸出還沒接剝離器(exec 已接),
同款接線列下一輪。附:對手的掛死/stderr-help/零核准都記錄在案,
不是為了嘲笑,是為了知道「同級」的真實含義。
R3.1 原案「雙串流迴圈合一」——大手術,先寫設計。偵察的第一刀
就改寫了手術方案:stream_agent_full 在全 repo 零生產呼叫者。
只有 streaming.rs 自身、agent.rs 兩條化石註解(「serve/partner 用」
——早遷走了沒刪),和兩個測試檔在養它。
所謂「兩套迴圈」,實況是一套活的(agent.rs,daemon+CLI 實證,
本週所有修復都在裡面)+ 一套殭屍。合併=把殭屍 800 行逐段搬進
活體,風險高收益零;正解是退役:殭屍迴圈刪除、streaming.rs
縮身為串流工具箱(salvage/strip/trait——這些是真被用的)、
測試逐條裁決遷移。設計文寫成 plan/R3-雙迴圈收斂設計.md,
已派 codex 複檢(指令:自己 grep 驗證零呼叫者,別信文件)。
教訓预告:債的名字會說謊。「雙迴圈收斂」聽起來像整合工程,
量完才知道是葬禮。動刀前先數屍體,跟動手前先數自己有什麼,
是同一條紀律。
同輪小債清畢:repl -c 接上殘渣剝離器(od 驗證 9\n),
exec 與 repl 兩個 one-shot 面現在同款衛生。
第一幕,精準摘除。 主家族四口(stream_agent_full 兩兄弟、
stream_agent、stream_one_round)按名字定位、brace 配平摘除,
編譯器接力點名佃農(URL 路由副本、pre-stream 重試、frame 處理器
——每一個都是活體 agent.rs 已有的知識的殭屍副本),迭代清到
never-used 歸零。
第二幕,兩次翻車。 手寫的 brace 配平被字串字面值裡的大括號
騙了兩次——一次把檔案砍到 77 行,一次摘 extract_stream_error 吃掉
兩千行。教訓刻進骨頭:手寫 parser 對 Rust 原始碼不可靠,
配平會被 "{" 字面值毒死。第三次改用「保留式重建」:活體符號
(salvage、strip、StreamEvent、StreamResult、ResolveProvider)
全在檔案前 716 行,salvage_tests 在尾部——新檔=頭+墓誌銘+尾,
一次成型。git 是安全網:兩次翻車都 checkout 重來,零損失。
第三幕,陪葬審查。 殭屍測試 80 條逐類審過才准死:backoff/
retry_after/frame 處理器測的函式已亡;「URL 路由」那批藏著
mistral 401 的修痕——查活體:resolver.rs 已有等價路由表與
mistral_url_routes_to_native 測試,陪葬正確。遷移檔
streaming_trait_migration 的 resolver 注入契約,活體
agent_with_resolver.rs 已覆蓋——刪檔。
出口驗收:lib 2631 綠(-80=陪葬數)、七整合檔綠、golden 89
全過、release 部署、daemon 實測 ONE-LOOP-OK。
streaming.rs 從 3722 行縮到 866 行,留下的每個符號都有
真 caller。一套迴圈,一個真相源。
(誤殺虛驚一場也記錄:迭代腳本曾把「引用 ProviderEntry 的測試」
連坐摘除——逐條回審,受測物全是亡者,無誤殺;但判準是
「引用了找不到的符號」而非「受測物已亡」,命中靠運氣,記過。)
建 cli_slash::SLASH_REGISTRY(name / scope Repl|Tui|Both / usage),
兩介面清單各自成為 registry 的投影,雙向對帳 ratchet 釘死。
對帳測試上線的第一分鐘就抓到兩件事:TUI 實際清單比我手抄的多
十條(/broadcast /fanout /priority /sidebar…),以及——/resume 在 TUI 清單裡宣告了兩次,重複宣告躺了不知多久,
沒有任何機制會發現它,直到有一張表逼兩邊對帳。
/fork 進 TUI:仿 /resume 的切換機制(fork_at 全量複製→切
chat_id→清 transcript→掛 pending_interrupt 防前 session 的 token
滲流)。REPL 獨有了很久的招牌,現在兩面都有;registry 的/fork scope=Both + ratchet 保證它再也不會單面消失。
R3.3(命令表審計)判定:宣告=實作=求助已由 R0.1 的
declared_subcommands_help ratchet 覆蓋、文件片段有
documented_scripts_are_runnable、斜線層本輪補齊——三方一致的
主線閉環,usage 欄位補全列為小債。R3 全收。
R4.1 詞彙裁定(一頁定案):「ensemble」單獨出現=in-repo crew
編排(正宗);多評審機制改稱 judge panel;外部 ensemble binary 退場。
R4.2 serve 切軌:/api/mesh/dev 原本 PATH 探測外部 ensemble
執行檔再 shell-out——現在直接跑 in-repo crew。切軌三原則:
(1) SSE 表面不變(stdout/stderr/exit_code 事件形狀相同,app 的
MeshOps 頁零改動);(2) crew 的 Blackboard 訊息經 RunObserver 流式
進 SSE([role/kind] body);(3) 不再自動 merge——crew 的工作
留在 spectyn-crew/ 分支,落地是人的決定。組裝邏輯同步提取為crew::assemble_adapters + DEFAULT_CREW_TOML,CLI 與 serve
同源,不存在第三份副本。ratchet:serve.rs 永不得再出現
find_ensemble_exe(源碼掃描測試釘死)。
(範圍註記:serve 裡另有一條「遠端節點 HTTP ensemble 服務」軌
(ensemble_base_url)——那是 mesh 節點間協定,另一場手術,如實保留並記錄。)
R4.3 worktree 隔離:crew::run_in_worktree——git worktree add
臨時分支→conductor 在隔離樹裡跑→呼叫者收成果後 remove,分支保留。
單元測試:worktree 位於 .spectyn-crew/、內容齊、呼叫者工作樹
一位元組不動、移除乾淨。crew 從此不在你的 cwd 裡動手。
lib 2636 綠、golden 89 全過。R4.4 實測(帶領/被帶領)下一輪專場。
我自己開 spectyn mcp 的 stdio,走完整協定:
initialize → spectyn-mesh 0.6.0
tools/list → 61 個工具
file_read → mcp-target: 42 (讀對)
glob_search → 找到 target.txt (cwd 感知)
memory_store → [denied] trust enforcement=observe
memory_search→ 誠實回「無此項」
最後兩行是加分題:零設定沙盒的 Observe 唯讀夾制,在 MCP
被帶領情境同樣執法——寫入被拒、後續查詢誠實反映「沒存進去」。
安全故事跨介面一致:CLI、daemon、MCP,同一套信任閘。
「其他 AI 帶領 spectyn」實測成立。